package cn.lingyangwl.framework.log;

import cn.lingyangwl.framework.core.http.HttpRequestServletManager;
import cn.lingyangwl.framework.core.utils.IpUtils;
import cn.lingyangwl.framework.core.utils.servlet.ServletUtils;
import com.alibaba.fastjson2.JSON;
import com.google.gson.Gson;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.time.StopWatch;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.validation.BindingResult;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 全局日志打印拦截器
 *
 * @author shenguangyang
 */
@Slf4j
@Aspect
@Component
@Order(Ordered.HIGHEST_PRECEDENCE + 10)
public class WebLogAspect {
    @Resource
    private LyLogProperties logProperties;

    @Pointcut("execution(public * *..*.*Controller.*(..))")
    public void webLog() {
    }

    @Around("webLog()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        if (!logProperties.getEnableWebLog()) {
            return point.proceed();
        }
        Object result = null;
        WebLogInfo.WebLogInfoBuilder builder = WebLogInfo.builder().status("ok");
        StopWatch watch = StopWatch.createStarted();
        try {
            result = point.proceed();
            return result;
        } catch (Throwable e) {
            builder.status("fail").errorMsg(e.getMessage());
            throw e;
        } finally {
            try {
                HttpServletRequest request = ServletUtils.getRequest().orElseThrow(() -> new RuntimeException("request is null"));
                // 请求的地址
                String ip = IpUtils.getRequestIp(new HttpRequestServletManager(request));

                builder.ip(ip).url(request.getRequestURI()).respData(new Gson().toJson(result))
                        .costTime(watch.getTime(TimeUnit.MILLISECONDS)).httpMethod(request.getMethod());
                watch.stop();

                // 参数值
                Object[] argsValue = point.getArgs();
                builder.reqData(JSON.toJSONString(argsArrayToList(argsValue)));

                WebLogInfo webLogInfo = builder.build();
                webLogInfo.printfLog();
            } catch (Exception e) {
                log.error("error: ", e);
            }
        }
    }

    /**
     * 参数拼装
     */
    private List<Object> argsArrayToList(Object[] paramsArray) {
        if (paramsArray != null && paramsArray.length > 0) {
            return Arrays.stream(paramsArray).filter(Objects::nonNull).filter(e -> !isFilterObject(e)).collect(Collectors.toList());
        }
        return Collections.emptyList();
    }

    /**
     * 判断是否需要过滤的对象。
     *
     * @param o 对象信息。
     * @return 如果是需要过滤的对象，则返回true；否则返回false。
     */
    @SuppressWarnings("rawtypes")
    public boolean isFilterObject(final Object o) {
        return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse
                || o instanceof BindingResult;
    }
}
